import SVGGraphics from "../class/SVGGraphics";
import TWEEN from "../class/Tween";
import Food, { FoodContainer, FoodEffect} from "./Food";
import {renderer,
    LineAnimate,
    S_ANI_TIME,
    cbrt,
    segmentsIntr,
    getPointToLineDis,
    getAnchors,
    movePointWithDirection
} from "./common";
interface Skin {
    body_colors: number[],
    head_url: string,
    head_effective_width?: number,
    head_anchor?: Point
}
const skinMap: { [key: string]: Skin } = {
    "rainbow": {
        body_colors: [0x00a8df, 0x00a8df, 0x127cc3, 0x1d63b4, 0x274da5, 0x2d3f9d, 0x343395
            , 0x492f93, 0x642a91, 0x702790, 0x88228e, 0x94208c, 0xa31d8b, 0xb81989, 0xcb1782
            , 0xd01a76, 0xd41d6b, 0xd51e6a, 0xe0264a, 0xe72b38, 0xeb312d
            , 0xf1732d, 0xf4942d, 0xf8b52d, 0xf9c02d, 0xfacf2d, 0xfbde2d, 0xf8e72d
            , 0xdade32, 0xc1d736, 0x99cb3c, 0x6dbe43, 0x54b747, 0x31ac4c, 0x0ea251
            , 0x0ea251, 0x00a293, 0x00a5c1],
        head_url: "/images/snake_head.png",
        head_effective_width: 160
    },
    "test_v2": {
        body_colors: (() => {
            var colors = [
                0xcb3f3f,
                0xcd4544,
                0xcf4c4b,
                0xd45655,
                0xda6868,
                0xdd7170,
                0xdf7676,
            ]
            return colors.concat(colors.slice().reverse())
        })(),
        head_url: "./images/skin/test_v2/head.png",
        head_effective_width: 380,
        head_anchor: { x: 0.5, y: 0.625 }
    }
};
var skinNames = Object.keys(skinMap);

export interface SnakeShape {
    /**头部贴图 */
    head?: string,
    body: {
        offset: {
            x: number,
            y: number
        },
        length: number,
        points: { x: number, y: number }[],
    },
    /**密度：密度越大，就能越快地开始进行转向，同时显示得越瘦小 */
    density: number,
    /**速度：单位时间行走量，是蠕动周期的另外一种值 */
    speed: number,
    /**灵敏度:转向速度 */
    sensitivity: number,
    /**尾部贴图 */
    tail?: string,
    /**方向 */
    direction: number,
    /**皮肤 */
    skin?: string
}
interface Point {
    x: number,
    y: number
}
const MaxKnotTexWidth = 200;

/**单位时间内的偏移量*/
const base_speed = 5;
/**两点之间基础间隔 */
// const base_spacing = 5;
/**单位时间内的旋转量*/
const base_deg = 36;

// 将要回收的点，可进行重复利用
const CG_points: PIXI.Point[] = [];

function popCGPoint() {
    return CG_points.pop() || new PIXI.Point();
};
function pointDis(point_1: Point, point_2: Point) {
    return Math.sqrt(Math.pow(point_2.x - point_1.x, 2) + Math.pow(point_2.y - point_1.y, 2))
};

/**获取对应的边缘点 */
function getSizePoints(pre_point, cur_point, nex_point, distance) {
    var base_direction_x = nex_point.x - pre_point.x;
    var base_direction_y = nex_point.y - pre_point.y;
    var rate_x_y = base_direction_x / base_direction_y;
    var rate_y_x = base_direction_y / base_direction_x;
    var res_direction_x = Math.sqrt(Math.pow(distance, 2) / (1 + Math.pow(rate_y_x, 2)));
    var res_direction_y = Math.sqrt(Math.pow(distance, 2) / (1 + Math.pow(rate_x_y, 2)));
    base_direction_x < 0 && (res_direction_x = - res_direction_x);
    base_direction_y < 0 && (res_direction_y = - res_direction_y);

    var left_size_point = popCGPoint();
    left_size_point.set(
        cur_point.x - res_direction_y
        , cur_point.y + res_direction_x
    );
    var right_size_point = popCGPoint();
    right_size_point.set(
        cur_point.x + res_direction_y
        , cur_point.y - res_direction_x
    );

    return [
        left_size_point,
        right_size_point,
    ];
}

const getLineLength = (points) => {
    var pre_point;
    var total_body_length = 0;
    for (var cur_point of points) {
        if (!cur_point) {
            break;
        }
        if (!pre_point) {
            pre_point = cur_point;
            continue;
        }
        total_body_length += Math.sqrt(Math.pow(cur_point.x - pre_point.x, 2) + Math.pow(cur_point.y - pre_point.y, 2))
        pre_point = cur_point;
    }
    return total_body_length;
}
export default class Snake extends PIXI.Container {
    _id: string
    /**是否开启帧平滑 */
    _is_use_pre_points: boolean
    /**帧平滑的平滑帧数 */
    private _pre_points_fps: number
    private _pre_points_fps_i: number
    _base_spacing: number = 5;
    _pre_points: PIXI.Point[]//保持上一帧的店，用来实现平滑过度
    _skin: Skin
    private _path = (() => {
        var pathNode = document.createElementNS("http://www.w3.org/2000/svg", "path")
        pathNode.setAttribute("stroke", "#0F0");
        pathNode.setAttribute("stroke-width", "5");
        return pathNode
    })()
    updateRopePoints() {
        var _cur_points = this._body.points;
        var points: Point[] = _cur_points;
        var rope = this._body.con;
        const texture = rope.texture;
        const base_spacing = this._base_spacing;
        if (texture.height) {
            const skinData = this._skin.body_colors;
            // if (!texture["is_scaled"]) {
            //     texture["is_scaled"] = true;
            var source_tex_height = texture.height;
            var density = this._density;
            var target_tex_height = density * 2
            texture.height = target_tex_height;
            texture.width = texture.width * target_tex_height / source_tex_height;
            this._resizeHead();
            // }
            /** */
            // try {
            var _pre_points = this._pre_points
            if (_pre_points && this._is_use_pre_points) {
                var _pre_points_fps_i = this._pre_points_fps_i += 1;
                var _pre_points_fps = this._pre_points_fps;
                this._pre_points = points = LineAnimate(_pre_points, _cur_points, _pre_points_fps_i / _pre_points_fps);
                if (_pre_points_fps_i >= _pre_points_fps) {
                    this._is_use_pre_points = false
                }
            } else {
                this._pre_points = points = this._pre_points ? LineAnimate(this._pre_points, _cur_points, 0.5) : _cur_points;
                // this._pre_points = points = _cur_points;
            }

            // } catch (e) {
            //     debugger
            // }
            var pathNode = this._path;
            pathNode.setAttribute("d", points.map((point, i) => {
                // if (isNaN(point.x * point.y)) {
                //     debugger
                // }
                if (i === 0) {
                    return "M " + point.x + " " + point.y + "C " + point.x + " " + point.y
                } else if (i < points.length - 1) {
                    var pre_point = points[i - 1]
                    var nex_point = points[i + 1]
                    var a = getAnchors(pre_point.x, pre_point.y, point.x, point.y, nex_point.x, nex_point.y)
                    if (isNaN(a.x1)) {
                        return ""
                    }
                    return a.x1 + " " + a.y1 + " " + point.x + " " + point.y + " " + a.x2 + " " + a.y2
                } else {//最后一个点
                    return point.x + " " + point.y + " " + point.x + " " + point.y
                }
            }).join(" "));

            var tex_width = texture.width;
            var snake_body_length = this._body.length;//points.length * base_spacing;
            var point_childrens = rope.children;
            var res_points: Point[] = [];
            for (var total_path_length = pathNode.getTotalLength(), l = total_path_length, unit_l = base_spacing / 1.2 * total_path_length / snake_body_length, i = 0, total_i = Math.ceil(total_path_length / unit_l); l > 0 || ((l = 0) || true); l -= unit_l, i += 1) {
                var cur_point = pathNode.getPointAtLength(l);
                res_points.push(cur_point)
                var inv_i = total_i - i;
                var inv_index = point_childrens.length - 1 - i;
                if (inv_index < 0) {
                    var point_sprite = new PIXI.Sprite(PIXI.Texture.fromImage("/images/snake_body_unit_v3.png"))
                    point_sprite.anchor.set(0.5, 0.5)
                    point_sprite.tint = skinData[i % skinData.length];
                    rope.addChildAt(point_sprite, 0);
                } else {
                    var point_sprite = <PIXI.Sprite>point_childrens[inv_index];
                }
                point_sprite.width = target_tex_height;
                point_sprite.height = target_tex_height;
                point_sprite.position.set(cur_point.x, cur_point.y);
                if (this.is_sprint) {
                    point_sprite.blendMode = PIXI.BLEND_MODES.ADD
                } else {
                    point_sprite.blendMode = PIXI.BLEND_MODES.NORMAL
                }
                if (l <= 0) {
                    for (var c_len = point_childrens.length, i = i + 1; i < c_len; c_len -= 1) {
                        rope.removeChildAt(0);
                    }
                    break
                }
            }
            // for (var i = 0; i < points.length; i += 1) {
            //     var cur_point = points[i];
            //     res_points.push(cur_point)
            //     var point_sprite = <PIXI.Sprite>point_childrens[i];
            //     if (!point_sprite) {
            //         point_sprite = new PIXI.Sprite(PIXI.Texture.fromImage("/images/snake_body_unit_v3.png"))
            //         point_sprite.anchor.set(0.5, 0.5)
            //         point_sprite.tint = skinData[i % skinData.length];
            //         rope.addChild(point_sprite);
            //     }
            //     point_sprite.width = target_tex_height;
            //     point_sprite.height = target_tex_height;
            //     point_sprite.position.set(cur_point.x, cur_point.y);
            // }
            // for (var c_len = point_childrens.length, i = i + 1; i < c_len; c_len -= 1) {
            //     rope.removeChildAt(i);
            // }
            const renderPoints = (g) => {
                var childs = g.children
                if (!childs.length) {
                    var g1_line = new PIXI.Graphics();
                    var g1_point = new PIXI.Graphics();
                    g.addChild(g1_line);
                    g.addChild(g1_point);
                    var g2_line = new PIXI.Graphics();
                    var g2_point = new PIXI.Graphics();
                    g.addChild(g2_line);
                    g.addChild(g2_point);
                    var g3_path = new PIXI.Graphics();
                    g3_path["svg_help"] = new SVGGraphics(g3_path);
                    g.addChild(g3_path);
                } else {
                    var g1_line = <PIXI.Graphics>childs[0]
                    var g1_point = <PIXI.Graphics>childs[1]
                    var g2_line = <PIXI.Graphics>childs[2]
                    var g2_point = <PIXI.Graphics>childs[3]
                    var g3_path = <PIXI.Graphics>childs[4]
                    g1_line.clear();
                    g1_point.clear();
                    g2_line.clear();
                    g2_point.clear();
                    g3_path.clear();
                }

                g1_line.lineStyle(.5, 0xffc2c2);
                g1_point.lineStyle(.5, 0xffc2c2);
                g1_line.moveTo(points[0].x, points[0].y);

                for (var i = 0; i < points.length; i++) {
                    if (i > 0) {
                        g1_line.lineTo(points[i].x, points[i].y);
                    }
                    g1_point.beginFill(0xff0022);
                    g1_point.drawCircle(points[i].x, points[i].y, 2);
                    g1_point.endFill();
                };

                // g2_line.lineStyle(.5, 0xc2c2ff);
                // g2_point.lineStyle(.5, 0xc2c2ff);
                // g2_line.moveTo(res_points[0].x, res_points[0].y);

                // for (var i = 0; i < res_points.length; i++) {
                //     if (i > 0) {
                //         g2_line.lineTo(res_points[i].x, res_points[i].y);
                //     }
                //     g2_point.beginFill(0x0022ff);
                //     g2_point.drawCircle(res_points[i].x, res_points[i].y, 2);
                //     g2_point.endFill();
                // };

                // var svg_help = <SVGGraphics>g3_path["svg_help"];
                // svg_help.drawNode(pathNode);
            };
            // renderPoints(this._body.chain);
        }

    }
    private _is_WFID
    waitingForImportData() {
        this._ANI.Tween("turnMySnakeHeadWithSlowDown", this)
            .to({
                _speed: this._speed / 10
            }, S_ANI_TIME)
            .start();
        this._is_WFID = true;
    }
    /**头部 */
    _head: PIXI.Sprite
    _body: { points: PIXI.Point[], length: number, con: PIXI.mesh.Mesh, chain: PIXI.Graphics } = {
        points: [],
        // con: new PIXI.ParticleContainer(0, {
        //     scale: true,
        //     position: true,
        //     rotation: true,
        //     uvs: true,
        //     alpha: true
        // })
        length: 0,
        con: new PIXI.mesh.Mesh(PIXI.Texture.fromImage("./images/snake_body.jpg"), new Float32Array([]), new Float32Array([]), new Uint16Array([])),
        chain: new PIXI.Graphics()
    }//PIXI.mesh.Rope = new PIXI.mesh.Rope(PIXI.Texture.fromImage("/images/snake.jpg"), [])
    /**密度：密度越大，就能越快地开始进行转向，同时显示得越瘦小 */
    _density: number
    /**速度：单位时间行走量，是蠕动周期的另外一种值，单位：像素/秒 */
    _speed: number
    /**灵敏度:转向速度，单位：度/秒 */
    _sensitivity: number
    _tail: PIXI.Sprite
    _ANI = new TWEEN()
    /**身体蠕动曲线信息 */
    private _line = {
        length: 0
    }
    /**方向：[0~360) */
    _direction: number
    /**可食用的食物集合 */
    _foods: FoodContainer
    is_ignore: boolean = false
    is_stop: boolean = false
    is_sprint: boolean = false
    /**尾部延迟增长 */
    _delay_num = 0;
    /**方向延迟增长 */
    _delay_deg = 0;
    /**方向旋转的时候是否顺时针 */
    _is_clockwise = true;
    _light = new PIXI.lights.PointLight(0xffffff, 2)
    constructor(snake_shape: SnakeShape) {
        super();
        this._delay_num = snake_shape.body.length;

        this.addChild(this._body.con);
        this.importShape(snake_shape);
        // var snake_body_tex = PIXI.Texture.fromImage("./images/snake_body.png");
        // if (snake_body_tex.hasLoaded) {

        // }
        this.addChild(this._body.chain);
        this._body.chain.alpha = 0.5;
        // this._body.chain.scale.set(2,2);
        // this.addChild(this._light);

        this._ani_move();
    }
    private _ani_move() {
        var _pre_time = Date.now();
        var snake = this;
        // requestAnimationFrame(function _ani() {
        //     var _cur_time = Date.now();
        //     snake.update(_cur_time - _pre_time);
        //     _pre_time = _cur_time;

        //     requestAnimationFrame(_ani);
        // });
        requestAnimationFrame(function _ani() {
            requestAnimationFrame(_ani);
            var _cur_time = Date.now();
            var _dif_time = _cur_time - _pre_time;
            snake.update(_dif_time);
            snake.updateDirection(_dif_time);
            _pre_time = _cur_time;
        });
    }
    update(delay_time) {
        if (this.is_stop) {
            return
        }
        const base_spacing = this._base_spacing;
        // 这条蛇进行一次偏移所需要的时间
        const unit_time = base_speed / this._speed * 100;
        // 这次运动计算的偏移比例
        const p = delay_time / unit_time;
        var points = this._body.points

        var source_distance = base_speed * p;
        var current_delay_num = this._delay_num;

        var distance = source_distance;
        if (current_delay_num < 0) {
            if (-current_delay_num >= source_distance) {
                var distance = 0;
                current_delay_num += source_distance
            } else {
                var distance = source_distance + current_delay_num;
                current_delay_num = 0;
            }
        }
        if (distance) {
            for (var len = points.length, cur_index = len - 2, nex_index = cur_index + 1; distance > 0; cur_index += 1, nex_index = cur_index + 1) {
                var nex_point = points[nex_index];
                var cur_point = points[cur_index];
                var point_dis = 0;

                if (nex_point) {
                    point_dis = Math.sqrt(Math.pow(nex_point.x - cur_point.x, 2) + Math.pow(nex_point.y - cur_point.y, 2));
                }
                /**
                 * 距离为空有两种情况，
                 * 一种是：最后两个顶点之间没有距离，就无法确定xy_rate
                 * 一种是：nex_index > points.length - 1，属于超过最后一个顶点的新店
                 */
                if (!point_dis) { //使用_direction属性
                    var xy_rate = Math.tan((this._direction / 180) * Math.PI);
                    var symbol_x = this._direction < 180 ? 1 : -1;
                    var symbol_y = this._direction > 90 && this._direction < 270 ? -1 : 1;
                } else { //依据两点之间本有的方向
                    var xy_rate = (nex_point.x - cur_point.x) / (nex_point.y - cur_point.y);
                    var symbol_x = nex_point.x > cur_point.x ? 1 : -1;
                    var symbol_y = nex_point.y > cur_point.y ? 1 : -1;
                }
                if (!nex_point) { //没顶点或者顶点距离为0，或者是头部的顶点，直接使用方向来操作

                    nex_point = popCGPoint();
                    points.push(nex_point);

                    var move_dis = Math.min(distance, base_spacing);
                    distance -= move_dis;

                    // } else if (point_dis === base_spacing) { //间距刚好，直接跳过这个点
                    //     continue;
                    // } else if (point_dis > base_spacing) { //当前两点之间的距离过大，校准距离
                    //     var move_dis = base_spacing; //这个点后移
                    //     distance += point_dis - base_spacing; //增加偏移量
                } else if (point_dis >= base_spacing) { //间距刚好，直接跳过这个点
                    continue;
                } else { //两点之间有可增长的空隙
                    var move_dis = Math.min(distance + point_dis, //如果增长空间足够用
                        base_spacing);
                    distance -= move_dis - point_dis;
                }
                var xy_rate_pow_2 = xy_rate * xy_rate;
                var add_x = symbol_x * move_dis / Math.sqrt(1 + 1 / xy_rate_pow_2);
                var add_y = symbol_y * move_dis / Math.sqrt(1 + xy_rate_pow_2);

                var nex_x = cur_point.x + add_x;
                var nex_y = cur_point.y + add_y;
                if (nex_x === cur_point.x && nex_y === cur_point.y) {//如果这次增长两点之间的距离实在太短，不管，直接移除这个点
                    points.pop();
                } else {
                    nex_point.x = nex_x;
                    nex_point.y = nex_y;
                }

            }
        }


        var distance = source_distance;
        if (current_delay_num > 0) {
            if (current_delay_num >= source_distance) {
                var distance = 0;
                current_delay_num -= source_distance
            } else {
                var distance = source_distance - current_delay_num;
                current_delay_num = 0;
            }
        }

        if (distance) {
            for (var cur_index = 1, pre_index = 0, len = points.length; distance > 0; cur_index += 1, pre_index = cur_index - 1) {
                var pre_point = points[pre_index];
                var cur_point = points[cur_index];
                var point_dis = Math.sqrt(Math.pow(cur_point.x - pre_point.x, 2) + Math.pow(cur_point.y - pre_point.y, 2));
                var move_dis = Math.min(distance, base_spacing); //所要缩短的距离
                if (move_dis >= point_dis) { //点距离不够，移除点
                    distance -= point_dis;

                    CG_points.push(pre_point);
                    points.splice(pre_index, 1);
                    cur_index -= 1;
                    // console.log("remove point:", distance, point_dis);
                } else { //点距离溢出，缩短点之间的距离
                    distance -= move_dis;

                    var xy_rate_pow_2 = Math.pow((cur_point.x - pre_point.x) / (cur_point.y - pre_point.y), 2);
                    var symbol_x = pre_point.x > cur_point.x ? 1 : -1;
                    var symbol_y = pre_point.y > cur_point.y ? 1 : -1;
                    var cut_x = symbol_x * (point_dis - move_dis) / Math.sqrt(1 + 1 / xy_rate_pow_2);
                    var cut_y = symbol_y * (point_dis - move_dis) / Math.sqrt(1 + xy_rate_pow_2);

                    // console.log("cut_x:", [cur_point.x, pre_point.x, cut_x], "cut_y:", [cur_point.y, pre_point.y, cut_y]);
                    pre_point.x = cur_point.x + cut_x;
                    pre_point.y = cur_point.y + cut_y;
                    break;
                }
            }
        }

        var body_length = this._body.length += this._delay_num - current_delay_num;
        this._delay_num = current_delay_num;
        this._density = cbrt(body_length * 2) + 12;
        this._base_spacing = cbrt(body_length * 2);

        const snake_head = this._head
        if (snake_head) {
            var head_point = points[points.length - 1];
            snake_head.x = head_point.x
            snake_head.y = head_point.y
            this._light.position.set(head_point.x, head_point.y);
        }

        this.updateRopePoints();
    }
    importShape(snake_shape: SnakeShape) {
        var skin = this._skin = (snake_shape.skin && skinMap[snake_shape.skin]) || skinMap[skinNames[Math.floor(Math.random() * skinNames.length)]]
        this._setHead(skin.head_url, skin.head_effective_width, skin.head_anchor);
        this._setTail(snake_shape.tail);

        var offset = snake_shape.body.offset;
        this.x = offset.x;
        this.y = offset.y;
        var points = snake_shape.body.points.map((point) => {
            return new PIXI.Point(point.x, point.y)
        });
        // updateRopePoints(this._body, points);
        this._body.points = points;
        this._density = snake_shape.density || cbrt(snake_shape.body.length * 2) + 12;
        this._speed = snake_shape.speed;
        this._sensitivity = snake_shape.sensitivity;
        this._direction = snake_shape.direction;
        this.updateRopePoints();
    }
    importData(snake_data) {
        if (this._is_WFID) {
            this._is_WFID = false;
            this._ANI.Tween("turnMySnakeHeadWithSlowDown").stop();
            //开启平滑帧
            this._is_use_pre_points = true;
            this._pre_points_fps = 10;
            this._pre_points_fps_i = 0;
        }

        var body_points = this._body.points;
        for (var i = 0, point_info; point_info = snake_data.points[i]; i += 1) {
            var current_point = body_points[i];
            if (!current_point) {
                current_point = body_points[i] = popCGPoint();
            }
            current_point.copy(point_info);
        }
        if (i < body_points.length) {
            CG_points.push.apply(CG_points, body_points.splice(i, body_points.length - i + 1));
        }
        this._body.length = snake_data.body_length;
        this._id = snake_data.id;
        this._density = snake_data.density || cbrt(snake_data.body_length * 2) + 12;
        this._speed = snake_data.speed;
        this._sensitivity = snake_data.sensitivity;
        this._direction = snake_data.direction;
        this._delay_num = snake_data.delay_num;

        //将要增长的长度
        if (snake_data.delay_num === undefined) {
            var current_body_length = this.getBodyLength();
            this._delay_num = this._body.length - current_body_length;
            this._body.length = current_body_length;
        } else {
            this._delay_num = snake_data.delay_num
        }
        this._delay_deg = snake_data.delay_deg;
        this._is_clockwise = !!snake_data.is_clockwise;
        this.is_ignore = !!snake_data.is_ignore;
        this.is_stop = !!snake_data.is_stop;
        this.is_sprint = snake_data.is_sprint;

        this.updateRopePoints();
    }
    getBodyLength() {
        const points = this._body.points;
        var pre_point;
        var total_body_length = 0;
        for (var cur_point of points) {
            if (!cur_point) {
                break;
            }
            if (!pre_point) {
                pre_point = cur_point;
                continue;
            }
            total_body_length += Math.sqrt(Math.pow(cur_point.x - pre_point.x, 2) + Math.pow(cur_point.y - pre_point.y, 2))
            pre_point = cur_point;
        }
        return total_body_length;
    }
    private _effective_width: number;
    private _setHead(head_url: string, effective_width?: number, anchor: Point = { x: 0.5, y: 0.5 }) {
        if (this._head) {
            this.removeChild(this._head);
            this._head = null;
        }
        if (head_url) {
            var tex = PIXI.Texture.fromImage(head_url);
            var head = this._head = new PIXI.Sprite(tex);
            head.anchor.copy(<PIXI.Point>anchor)
            this.addChild(head);
            tex.once("update", () => {
                this._effective_width = effective_width || tex.width;
                this._resizeHead();
            })
        }
    }
    private _resizeHead() {
        if (this._head) {
            var effective_width = this._effective_width;
            var scale = this._head.scale;
            scale.x = scale.y = this._density * 2 / effective_width;
        }
    }
    private _setTail(tail_url: string) {
        if (this._tail) {
            this.removeChild(this._tail);
            this._tail = null;
        }
        if (tail_url) {
            this._tail = PIXI.Sprite.fromImage(tail_url);
            this.addChild(this._tail)
        }
    }
    exportShape(): SnakeShape {
        let _offset = this.getBounds()
        return {
            head: this._head._texture.imageUrl,
            body: {
                offset: {
                    x: _offset.x,
                    y: _offset.y
                },
                length: this._body.length,
                points: this._body.points.map(point => { return { x: point.x, y: point.y } })
            },
            density: this._density,
            speed: this._speed,
            sensitivity: this._sensitivity,
            direction: this._direction
        }
    }
    private _c_r = ~~(Math.random() * 256)
    private _c_g = ~~(Math.random() * 256)
    private _c_b = ~~(Math.random() * 256)
    private _c_a = Math.min(Math.max(Math.random() + 0.3, 0.3), 1)
    private _c_thit = 65536/*256 * 256*/ * this._c_r + 256 * this._c_g + this._c_b
    private _knotGra = (() => {
        const knotGra = new PIXI.Graphics();
        knotGra.beginFill(0xffffff);
        knotGra.drawCircle(0, 0, MaxKnotTexWidth);
        knotGra.endFill();
        return knotGra;
    })()
    private _knotTexLen = 20;
    private _knotTexList = (() => {
        var _knotGra = this._knotGra
        /**节点周期为20 */
        var len = this._knotTexLen;
        var _thit = this._c_thit;
        var res = [];
        for (var i = 0; i < 20; i += 1) {
            var color_proportion = Math.abs((i % 20) / 20 - 0.5) + 0.5;//[0.5~0~0.5]
            _knotGra.tint = _thit * color_proportion;
            res.push(_knotGra.generateTexture(renderer))
        }
        return <PIXI.Texture[]>res;
    })()
    private _addBodyColorPoint(i) {
        var knot = new PIXI.Sprite(this._knotTexList[i % this._knotTexLen]);
        knot.width = knot.height = this._density * 2;
        knot.anchor.set(.5, .5);
        knot.alpha = this._c_a;

        this._body.con.addChild(knot);
        return knot;
    }
    /**身体成长 */
    addBody(num: number) {
        this._delay_num += num;
    }
    addSpeed(num: number) {
        this._speed += num;
        // this._ani_move();
    }
    addSensitivity(num: number) {
        this._sensitivity += num;
    }
    turnHead(deg: number) {
        var _direction = this._direction;
        var dif_deg = Math.abs(deg - _direction);
        if (!dif_deg) {
            return
        }
        var _is_clockwise = false;

        // 顺时针的角度范围
        var _left_deg_range = _direction - 180;
        if (_direction < 180) {
            if (deg < _direction || deg > _left_deg_range + 360) {
                _is_clockwise = true
            }
        } else {
            if (deg > _left_deg_range && deg < _direction) {
                _is_clockwise = true
            }
        }

        if (dif_deg > 180) {
            this._delay_deg = 360 - dif_deg;
        } else {
            this._delay_deg = dif_deg;
        }
        this._is_clockwise = _is_clockwise;
    }
    updateDirection(delay_time) {
        if (this.is_stop) {
            return
        }
        var _delay_deg = this._delay_deg;
        if (!_delay_deg) {
            return
        }
        // 这条蛇进行一次转向所需要的时间
        const unit_time = 360 / this._sensitivity * 100;
        // 这次运动计算的旋转角度
        const deg = delay_time / unit_time * base_deg;
        if (deg < _delay_deg) {
            this._delay_deg -= deg;
            var move_deg = deg;
        } else {
            this._delay_deg = 0;
            move_deg = _delay_deg;
        }

        var _direction = this._direction
        if (this._is_clockwise) {
            _direction -= move_deg;
            if (_direction < 0) {
                _direction += 360
            }
        } else {
            _direction += move_deg;
            if (_direction >= 360) {
                _direction -= 360
            }
        }
        this._direction = _direction;

        const snake_head = this._head;
        if (snake_head) {
            var res_rotation = ((_direction > 180) ? (540 - _direction) : Math.abs(_direction - 180)) / 180 * Math.PI
            snake_head.rotation = res_rotation;
        }
    }
    run(time?: number) {
        this._ANI.update(time);
        this._eatFoods();
        this._hitSnakes();
    }
    getPoint(point_index: number) {
        return this._body.points[point_index];
    }
    /**获取尾部对应的点 */
    getTailPoint() {
        return this._body.points[0];
    }
    /**获取头部对应的点 */
    getHeadPoint() {
        return this._body.points[this._body.points.length - 1];
    }
    /**投食：将可食用食物投入视野 */
    viewFoods(foods: FoodContainer) {
        this._foods = foods;
    }
    private _eatFoods() {
        const self = this;
        const foods = self._foods;
        if (foods) {
            const head = self.getHeadPoint();
            const density = self._density;
            var mouth = movePointWithDirection(self._direction, density, head);
            // self._head.position.set(mouth.x,mouth.y)
            const r_pow_2 = Math.pow(density + density, 2);
            var eated_food = foods.getCollisionItemWithCircle(mouth.x, mouth.y, density, (food, x, y) => {
                return (Math.pow(x - food.x, 2) + Math.pow(y - food.y, 2)) <= r_pow_2
            });
            var eated_points = [];
            eated_food.children_indexs.length && eated_food.children_indexs.forEach((remover_index, i) => {
                var food = <Food>foods.getChildAt(remover_index);
                eated_points.push(food);
                food.onEat(self);
            });
            eated_food.emiter_pos_indexs.length && eated_food.emiter_pos_indexs.forEach((remover_index, i) => {
                var eat_able_food = foods.removeParticleFoodAt(remover_index - i, {
                    get fly_to() {
                        return movePointWithDirection(self._direction, self._density, self.getHeadPoint());
                    }
                });
                eated_points.push(eat_able_food);
                Food.prototype.effectFood.call(eat_able_food, self)
            });
            if (eated_points.length) {
                self.emit("eated_food", eated_points);
            }
            foods.update();
        }
    }
    private _snakes: SnakeContainer
    /**竞争：将其它对手投入视野 */
    viewSnake(snakes: SnakeContainer) {
        this._snakes = snakes;
    }
    private _hitSnakes() {
        const snakes = this._snakes;
        if (snakes) {
            const head = this.getHeadPoint();
            const density = this._density;

            var hit_snake = snakes.getCollisionItem(head.x, head.y, (snake, point_index, x, y) => {
                var point = snake.getPoint(point_index);
                return !snake.is_ignore && (Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2)) <= Math.pow(density + snake._density, 2)
            });
            // this._foods && hit_snake.forEach(snake => {
            //     snake.is_ignore = true;
            //     snake.is_stop = true;
            //     snake._ANI.Tween(snake)
            //         .to({
            //             alpha: 0
            //         }, 1025)
            //         .onComplete(() => {
            //             snakes.removeChild(snake)
            //         })
            //         .start();
            //     snake.die(this._foods)
            // })
            if (hit_snake.length) {
                this.emit("died", hit_snake)
            }
            // console.log(hit_snake);
        }
    }
    // die(food_con: FoodContainer) {
    //     var points = this._body.points;
    //     var density = this._density;
    //     var total_body = points.length;
    //     var total_speed = this._speed;
    //     // var total_sensitivity = this._sensitivity;
    //     var unit_body = 1;
    //     var unit_speed = total_speed / total_speed;
    //     var unit_sensitivity = 2;//total_sensitivity / total_speed;

    //     var rem_body = total_body;
    //     var rem_speed = unit_speed * total_body;
    //     var rem_sensitivity = unit_sensitivity * total_body;

    //     var total_sensitivity = rem_sensitivity;
    //     points.forEach((point, i) => {
    //         var add_body = i * unit_body >= total_body - rem_body ? unit_body * Math.round(Math.random() * 2) : 0
    //         rem_body -= add_body;
    //         var add_speed = i * unit_speed >= total_speed - rem_speed ? unit_speed * Math.round(Math.random() * 2) : 0
    //         rem_speed -= add_speed;
    //         var add_sensitivity = i * unit_sensitivity >= total_sensitivity - rem_sensitivity ? unit_sensitivity * Math.round(Math.random() * 2) : 0
    //         rem_sensitivity -= add_sensitivity;

    //         food_con.addParticleFood(new FoodEffect({
    //             add_body: add_body,
    //             add_speed: add_speed,
    //             add_sensitivity: add_sensitivity,
    //         }), point.x + (Math.random() - 0.5) * 2 * density, point.y + (Math.random() - 0.5) * 2 * density)
    //     })
    // }
    die() {
        this.is_ignore = true;
        this.is_stop = true;
        this._ANI.Tween(this)
            .to({
                alpha: 0
            }, 1025)
            .onComplete(() => {
                this.parent && this.parent.removeChild(this)
            })
            .start();
    }
}

export class SnakeContainer extends PIXI.Container {
    children: Snake[]
    constructor() {
        super()
    }
    addSnake(snake: Snake) {
        this.addChild(snake);
    }
    getCollisionItem(x: number, y: number, filter: (snake: Snake, point_index: number, x: number, y: number) => boolean): Snake[] {
        return this.children.filter((snake: Snake) => {
            for (var i = 0, len = snake._body.points.length; i < len; i += 1) {
                if (filter(snake, i, x, y)) {
                    return true
                }
            }
            return false
        })
    }
}